package com.chmingx.JavaBasic.thread.collection;

import org.junit.Test;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 集合中的线程安全问题
 *
 * List中的add方法不是线程安全的
 *
 * 解决方案:
 * 1. Vector, 加了synchronized, 安全了，但是很慢
 * 2. Collections.synchronizedList, 虽然线程安全，但是很慢
 * 3. CopyOnWriteArrayList 推荐
 * 涉及的底层原理为写时复制技术
 *
 * 读的时候并发（多个线程操作）
 * 写的时候独立，先复制相同的空间到某个区域，将其写到新区域，旧新合并，并且读新区域（每次加新内容都写到新区域，覆盖合并之前旧区域，读取新区域添加的内容）
 *
 * 同样的HashSet和HashMap也是线程不安全的
 *
 * 可以使用CopyOnWriteArraySet, ConcurrentHashMap
 */
public class CollectionSafe {
    @Test
    public void testList() throws InterruptedException {
        // 创建ArrayList, 线程不安全
        // List<String> list = new ArrayList<>(); //java.util.ConcurrentModificationException 并发修改异常！

        // List<String> list = new Vector<>();

        // List<String> list = Collections.synchronizedList(new ArrayList<>());

        List<String> list = new CopyOnWriteArrayList<>();

        // 开启多线程，读写list
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            }, String.valueOf(i)).start();
        }

        Thread.sleep(1000);
    }

    @Test
    public void testSet() throws InterruptedException {
        // HashSet线程不安全
//        Set<String> set = new HashSet<>();

        // 解决方案
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 5; i++) {

            new Thread(() -> {
                set.add(UUID.randomUUID().toString());
                System.out.println(set);
            }, String.valueOf(i)).start();
        }

        Thread.sleep(1000);
    }

    @Test
    public void testMap() throws InterruptedException {
        // HashMap线程不安全
//        Map<String, Integer> map = new HashMap<>();

        // 解决办法
        Map<String, Integer> map = new ConcurrentHashMap<>();

        for (int i = 0; i < 5; i++) {
            Integer integer = Integer.valueOf(i);
            new Thread(() -> {
                map.put(UUID.randomUUID().toString(), integer);
                System.out.println(map);
            }, String.valueOf(i)).start();
        }

        Thread.sleep(1000);
    }
}
